| 1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859 |
- import { NextRequest, NextResponse } from 'next/server';
- import { fetchJson } from '@/lib/utils/server';
- import { isSocialLoginProvider, SOCIAL_PROVIDER_LABELS, OAUTH_STATE_COOKIE_PREFIX } from '@/constants/social';
- /**
- * 소셜 로그인 시작 라우트 (Naver/Kakao) — /login/{provider}/start
- *
- * 1. state 생성 (crypto.randomUUID) → httpOnly 쿠키 보관 (10분)
- * 2. 백엔드 GET /api/auth/{provider}/authorize-url 호출
- * 3. 각사 authorize 페이지로 302 리다이렉트
- *
- * Google 은 기존 GIS(id_token) 플로우(/login/google/*) 유지 — 이 라우트 미사용.
- */
- interface AuthorizeUrlResponse {
- provider: string;
- authorizeUrl: string;
- }
- export async function GET(request: NextRequest, { params }: { params: Promise<{ provider: string }> })
- {
- const { provider } = await params;
- const failRedirect = (message: string) => {
- const url = new URL('/login', request.url);
- url.searchParams.set('error', message);
- return NextResponse.redirect(url);
- };
- if (!isSocialLoginProvider(provider)) {
- return failRedirect('지원하지 않는 소셜 로그인입니다.');
- }
- const label = SOCIAL_PROVIDER_LABELS[provider];
- const state = crypto.randomUUID();
- const redirectUri = `${request.nextUrl.origin}/login/${provider}/callback`;
- const query = new URLSearchParams({ state, redirectUri });
- const res = await fetchJson<AuthorizeUrlResponse>(`/api/auth/${provider}/authorize-url?${query.toString()}`);
- if (!res.success || !res.data?.authorizeUrl) {
- // OAuth 앱 키 미설정(Auth.SocialNotConfigured)은 사용자 친화 문구로 치환
- if (res.errors?.some(e => e.code === 'Auth.SocialNotConfigured')) {
- return failRedirect(`${label} 로그인이 아직 준비되지 않았습니다. 잠시 후 다시 시도해주세요.`);
- }
- return failRedirect(res.errors?.[0]?.description || res.message || `${label} 로그인을 시작할 수 없습니다. 잠시 후 다시 시도해주세요.`);
- }
- const response = NextResponse.redirect(res.data.authorizeUrl);
- response.cookies.set(`${OAUTH_STATE_COOKIE_PREFIX}${provider}`, state, {
- httpOnly: true,
- path: '/',
- sameSite: 'lax',
- secure: process.env.NODE_ENV === 'production',
- maxAge: 600
- });
- return response;
- }
|